/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_CAMERA_H
#define M_CAMERA_H

#include "matrix4.h"
#include "refptr.h"
#include "aabb.h"
#include "common.h"
#include "types.h"
#include "renderable.h"
#include "renderdata.h"
#include "plane.h"

#include <vector>

namespace Engine
{
	/** Visibility types */
    enum Visibility
    {
        NONE,
        PARTIAL,
        FULL
    };

	/**
	*   Base class for all cameras.
	*   A viewpoint from which the scene will be rendered.
	*/
	class Camera
	{
		friend class SceneManager;
		friend class CameraRenderable;
	public:

		/**
		*   Destructor
		*/
		~Camera();

		/**
		*   Return objects name.
		*/
		const std::string& GetName();

		/**
		*   Sets the level of rendering detail required from this camera (wireframe for example).
		*/
		void SetPolygonMode(PolygonMode sd);

		/**
		*   Gets the level of rendering detail required from this camera (wireframe for example).
		*/
		PolygonMode GetPolygonMode()const {return m_polygonMode;}

		/**
		*   Sets the camera's view matrix.
		*/
		void LookAt(const Vector3<REAL>& pos, const Vector3<REAL>& dir, const Vector3<REAL>& up);

		/**
		*   Rotate around X
		*/
		void RotateX(REAL angle);

		/**
		*   Rotate around Y
		*/
		void RotateY(REAL angle);

		/**
		*   Move camera forward.
		*/
		void Move(REAL distance);

		/**
		*   Move camera left/right.
		*/
		void Strafe(REAL distance);

		/**
		*   Set cameras projection matrix.
		*/
		void SetPerspective(REAL viewAngle, REAL aspect, REAL near, REAL far);

		/**
		*   Tells the Camera to contact the SceneManager to render from it's viewpoint.
		*/
		void RenderScene(RenderTarget* renderTarget);

		/**
		*   Retrieve the number of visible faces in the last render.
		*/
		unsigned int GetNumRenderedFaces(void) const;

		/**
		*   Return current view matrix;
		*/
		const Matrix4<REAL>& GetViewMatrix() const;

		/**
		*   Return current projection matrix;
		*/
		const Matrix4<REAL>& GetProjMatrix() const;

		/**
		*   Notify camera of the visible faces in the last render.
		*   Called from scene manager;
		*/
		void NotifyRenderedFaces(unsigned int faces) const;

		/**
		*   Return true if bounding box is inside(even one corner) in camera frustum.
		*/
		Visibility GetVisibility(const AABB& bbox)const;

		/**
		*   Get camera position in world coordinates.
		*/
		const Vector3<REAL>& GetPosition()const;

		/**
		*   Stop recalculate culling frustum, and draw culling pyramid
		*/
		void EnableDebug(bool val);

		/**
		*   Populate render queue with culling pyramid
		*/
		void UpdateRenderQueue(RenderQueue* queue) const;

	private:

		/**
		*   Sets the camera's view matrix from local settings.
		*/
		void LookAt();

		/**
		*  Constructor.
		*/
		Camera(const std::string& name,SceneManager* sm);

		/**
		*   Recalculate frustum planes.
		*/
		void PlanesUpdate();


		typedef std::vector<Plane> PlanesVector;

		/**
		*   Worldspace clipping planes.
		*/
		enum FrustumPlane
		{
			FRUSTUM_PLANE_NEAR   = 0,
			FRUSTUM_PLANE_FAR    = 1,
			FRUSTUM_PLANE_LEFT   = 2,
			FRUSTUM_PLANE_RIGHT  = 3,
			FRUSTUM_PLANE_TOP    = 4,
			FRUSTUM_PLANE_BOTTOM = 5
		};

		/**
		*   Class for rendering culling pyramid
		*/
		class CameraRenderable: public Renderable
		{
		public:
			CameraRenderable(Camera* camera);
			~CameraRenderable();
			void Update();
			virtual const MaterialPtr GetMaterial(void) const;
			virtual RenderDataPtr GetRenderData() const;
			virtual const Matrix4<REAL>& GetWorldTransform() const;
			virtual const HardWareBufferPtr GetVertexData() const;
			virtual const HardWareBufferPtr GetIndexData() const;
			virtual const VertexDeclarationPtr GetVertexDeclaration() const ;
		private:
			Matrix4<REAL> m_transform;
			Camera* m_camera;
			MaterialPtr m_material;
			RefPtr<RenderData, WeakPtrExtension> m_renderData;
			RefPtr<VertexDeclaration,WeakPtrExtension> m_vertexDeclaration;
			RefPtr<HardWareBuffer,WeakPtrExtension> m_vertexData;
			RefPtr<HardWareBuffer,WeakPtrExtension> m_indexData;
		};

	private:
		Vector3<REAL>        m_position;
		Vector3<REAL>        m_dir;
		Vector3<REAL>        m_right;
		Vector3<REAL>        m_up;
		std::string          m_name;
		SceneManager*        m_sceneManager;
		Matrix4<REAL>        m_viewMatrix;
		Matrix4<REAL>        m_projMatrix;
		PolygonMode          m_polygonMode;
		mutable unsigned int m_renderedFaces;

		PlanesVector         m_planes;

		bool                 m_debugMode;
		RefPtr<CameraRenderable,WeakPtrExtension>     m_renderable;
	};
}

#endif

